Hotfix: gobby-hooks 0.1.1 (Windows extern block requires unsafe in edition 2024)#2
Hotfix: gobby-hooks 0.1.1 (Windows extern block requires unsafe in edition 2024)#2joshwilhelmi wants to merge 16 commits intomainfrom
Conversation
…grow output
When a named pipeline only trimmed a few bytes, gsqz still prepended the
~20-byte [gsqz:low-savings] marker, producing net-negative "savings" on
small outputs (e.g. short git diffs showing -1%). Now the marker is only
applied when the marked output is strictly smaller than the original;
otherwise the original output is returned unchanged with strategy_name
"{pipeline}/no-op". Existing low-savings test updated to a scenario where
the marker still fits; new test covers the no-op suppression path.
Canonical plan for making Gobby hooks work across Claude/Codex/Gemini/ QwenCode sandboxes. Introduces a new ghook crate as the fourth member of this workspace (alongside gcode, gsqz, gloc), plus runtime-detection on the Python daemon side so the two repos can ship independently. Companion task: gobby #11843 (bootstrap) and gobby #11842 (cleanup follow-up to remove the legacy hook_dispatcher.py path once ghook is universal. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> EOF )
PR 1 of the sandbox-tolerant-hooks Rust migration (R2-01 + R2-04 + R2-05). Scaffolds the shared gobby-core lib and ports find_project_root and read_project_id as public API. gcode keeps its local copy until PR 4 (R2-08) to keep this diff behavior-free. See docs/plans/sandbox-tolerant-hooks-rust.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ports hook_dispatcher.py:145-175 port-resolution into `bootstrap` + a new `daemon_url` module. Reads `daemon_port` and `bind_host` from `~/.gobby/bootstrap.yaml`; falls back to 60887 and 127.0.0.1 on missing or malformed files. `daemon_url` normalizes wildcard listen addresses (`0.0.0.0`, `::`, `::0`) to `127.0.0.1` for dialing — the dispatcher and gcode both silently broke when users set `bind_host: 0.0.0.0`. 13 unit tests cover default fallback, malformed YAML, custom port/host, and wildcard normalization. No consumers yet — ghook (PR 3) is first. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…atch Port hook_dispatcher.py to a native Rust binary with enqueue-first semantics: every invocation writes an envelope to ~/.gobby/hooks/inbox/<p>-<ts13>-<uuid>.json (fsync + rename) before the daemon POST, so sandbox FS-read denials and transient network failures never drop a hook — the drain worker replays from disk. CLI: --gobby-owned, --diagnose, --version. Exit 0 on success or non-critical failure (enqueued); 2 on critical failure (enqueued, signals host CLI). Malformed stdin goes directly to inbox/quarantine/ with a .meta.json sidecar (reason, json_error, stdin_bytes_b64) — drain never replays quarantined envelopes. Headers (X-Gobby-Project-Id, X-Gobby-Session-Id) are omitted entirely when unknown — never empty strings. Walk-up for project context runs before detach on Unix (setsid). Windows detach uses FreeConsole(); the spawn-time flags in the plan cannot be self-applied to a running process, and the enqueue-first file is the source of truth regardless. Terminal-context enrichment gated by per-CLI hook set; TMUX_PANE only emits when TMUX is also set to avoid parent/child pane confusion. Schemas frozen at v1 (inbox-envelope, diagnose-output), validated in unit tests. Release workflow mirrors release-gcode with tag prefix gobby-hook-v*; publishes to crates.io as gobby-hook. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lpers Remove duplicate find_project_root and read_project_id from gcode, now importing them from gobby_core::project. Updates call sites in config.rs and commands/index.rs. Deletes redundant tests (now tested in gobby-core). Adds gobby-core as a path dependency. Validation: - crates/gcode/Cargo.toml adds gobby-core path dependency - find_project_root and read_project_id removed from crates/gcode/src/project.rs - Call sites updated to import from gobby_core::project - Tests removed (behavior tested in gobby-core) - cargo test --workspace: PASS (148 tests) - cargo clippy --workspace --all-targets -- -D warnings: PASS - cargo fmt --all --check: PASS Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Mirrors updates made to the Python-side sandbox-tolerant-hooks-rev1
plan after the gobby-cli Phase 2 review:
- Windows detach spec corrected: `FreeConsole()` is the post-spawn
analog; `DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP` are
CreateProcess parent-side flags and cannot be self-applied from the
already-spawned child. Updated in both PR 3 description and the
contract-resolution section.
- Crate & distribution: documents the gobby-core → ghook publish-order
constraint, with the `version` + `path` dep declaration pattern that
lets workspace builds and crates.io both resolve.
- PR 3 CI bullet: explicit note that `release-gobby-core.yml` must
land in the repo first and the first `ghook` release is gated on
the matching `gobby-core` version being live on the registry.
- Empty-stdin normalization: ghook treats empty as `input_data = {}`;
dispatcher exits non-zero. Documented transition-window divergence;
exit code still governed by `--critical`, no silent drop.
- POST body shape: daemon endpoint accepts both legacy bare and
schema-v1 envelope. Python plan's §2.8 adds a handler test to pin
the tolerance.
…es on crates.io success gobby-hook depends on gobby-core as a path dep with a version; the release flow requires gobby-core to exist on crates.io first, so add a library-only release workflow keyed on gobby-core-v* tags. Also tighten release-gcode and release-ghook so the GitHub Release job waits on the crates.io step — prevents the footgun where binaries ship with a version that failed upstream. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s/gobby-core → crates/gcore
Two pre-release naming fixes, landed together while nothing is on crates.io
yet so the rename is free:
(A) Package gobby-hook → gobby-hooks. Binary (ghook), crate dir (crates/ghook),
and schema IDs unchanged. Convention: plural package name for domain
families (gobby-hooks, future gobby-tasks), singular binary for the tool
(ghook, gtask). Release tag prefix becomes gobby-hooks-v*.
(B) Directory crates/gobby-core → crates/gcore to match the g-prefix short-name
pattern (gcode, gsqz, gloc, ghook). Package name stays gobby-core — only
the filesystem layout changed. Workflow renamed release-gobby-core.yml →
release-gcore.yml for consistency with the other release-<short>.yml files.
Verified: cargo fmt, cargo clippy --workspace --all-targets -- -D warnings,
cargo test --workspace (267 passing), YAML syntax on all workflow files.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…n fts.rs LIKE escape Extract empty_response_for_unresolved helper in commands/graph.rs and use it from callers/usages/blast_radius. resolve_symbol_name in search/fts.rs no longer includes the resolved name in its suggestions vec — callers get only alternatives. LIKE fallback now escapes %/_/\ via new escape_like helper and uses ESCAPE '\' so symbol names containing underscores (snake_case) match literally. glob_to_like_prefix refactored to reuse escape_like; behavior unchanged for existing callers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…040) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…0.1.0, ghook 0.1.0 Bump crate versions, add new ghook user/dev guides and gcore dev guide, note gobby-core dependency in gcode dev guide and low-savings marker suppression in gsqz user guide, add four versioned CHANGELOG sections, and surface ghook + gcore in the top-level README. Adds version="0.1" to gcode's path dep on gobby-core so the manifest is valid for crates.io upload. Staged release order: tag gcore-v0.1.0 first (so gobby-core lands on the registry), then tag gcode-v0.6.0, gsqz-v0.4.1, ghook-v0.1.0 together. Verified: cargo build/test/clippy/fmt all clean across the workspace (267 tests passing). cargo package -p gobby-core succeeded; gobby-code packaging waits on gobby-core being on the registry (expected -- release ordering handles this). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…o-op strategy Follow-up to #111. The inner [gsqz:low-savings] marker is suppressed when adding it would grow output, but the outer "[Output compressed by gsqz -- STRATEGY, X% reduction]" header was still being prepended for the resulting */no-op strategy. That's noise -- no compression actually happened, so the header is just spurious. Adds CompressionResult::is_passthrough() classifying passthrough, excluded, and */no-op together. main.rs now uses it for both the outer-header decision and the daemon savings report so the two stay in sync. Adds a unit test for the classification and asserts the existing low-savings-suppressed test result also reports as passthrough. Rolls into the unreleased 0.4.1 -- no version bump. Tests: 149 in gsqz (+1 new), 268 across the workspace, all passing. Verified end-to-end: the compound-git command that previously emitted "git-mutation/low-savings, -1% reduction" now emits clean original output (strategy=git-mutation/no-op, savings=0.0%, no header). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ry_sort_by
Two pre-existing call sites in crates/gsqz/src/primitives/group.rs
trigger Rust 1.95.0's new clippy::unnecessary_sort_by lint. Local
toolchain is 1.94 so they slipped through; CI runs 1.95 and rejects
them. Mechanical replacement of
sort_by(|a, b| b.1.len().cmp(&a.1.len()))
with
sort_by_key(|b| std::cmp::Reverse(b.1.len()))
at lines 373 and 414. No behavior change. Rolls into the unreleased
gsqz 0.4.1 -- no version bump, no CHANGELOG entry (this is a CI
correctness fix, not a user-facing change).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ble_match
crates/gcode/src/commands/init.rs:34 trips clippy::collapsible_match
on a nested `match Err(e) => { if !quiet { ... } }`. Convert to a
match guard `Err(e) if !quiet => { ... }`; the `_ => {}` arm catches
the quiet=true case as before, so behavior is preserved.
Updated local toolchain to 1.95 (was 1.94) so all 1.95 lints fire
locally going forward. Verified all five CI clippy commands pass on
1.95: gobby-core, gobby-hooks, gobby-squeeze, gobby-local,
gobby-code (no embeddings). Rolls into unreleased gcode 0.6.0 -- no
version bump.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…e 0.1.1
ghook 0.1.0 published to crates.io but the Windows build target
failed: edition 2024 requires `extern "system" { ... }` to be
`unsafe extern "system" { ... }`. Local Mac dev never exercised the
#[cfg(windows)] branch in detach.rs so it slipped through.
Fix is one line in crates/ghook/src/detach.rs:36 plus a Cargo.toml
bump to 0.1.1 and a CHANGELOG entry. Linux/Mac use the
#[cfg(unix)] setsid(2) branch and were never affected -- the
crates.io 0.1.0 install works on those platforms but not Windows.
Verified: cargo build/test/clippy clean (27 ghook tests passing).
Once gobby-hooks-v0.1.1 is tagged, the Release ghook workflow
should succeed across all 5 build targets and create the GitHub
Release that 0.1.0 missed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughA new shared Changes
Sequence DiagramsequenceDiagram
actor Host_CLI
participant ghook_CLI as ghook (stdin mode)
participant Inbox as ~/.gobby/hooks/inbox/
participant Daemon as Gobby Daemon
Host_CLI->>ghook_CLI: stdin (JSON hook payload)
ghook_CLI->>ghook_CLI: Parse & validate JSON
ghook_CLI->>ghook_CLI: Discover project root
ghook_CLI->>ghook_CLI: Build Envelope struct
ghook_CLI->>Inbox: Atomic write (tmp + fsync + rename)
ghook_CLI->>ghook_CLI: Optional: detach from terminal
ghook_CLI->>Daemon: POST /api/hooks/execute
alt Success (2xx)
Daemon-->>ghook_CLI: 2xx response
ghook_CLI->>Inbox: Delete envelope file
ghook_CLI-->>Host_CLI: exit 0 (or 2 if critical)
else Failure (non-2xx/timeout)
Daemon-->>ghook_CLI: error/timeout
ghook_CLI->>Inbox: Leave envelope for replay
ghook_CLI-->>Host_CLI: exit 0 (or 2 if critical)
end
Note over Inbox,Daemon: Daemon drains inbox files<br/>on next startup/tick
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
Comment |
|
Reopening as PR from hotfix/ghook-0.1.1 \u2014 dev branch was based on the pre-rebase chain after PR #1 merged with rebase, so this PR's diff against main was conflicting. The fix is unchanged; new PR coming. |
There was a problem hiding this comment.
Pull request overview
This PR expands the workspace to include a new sandbox-tolerant hook dispatcher (ghook / gobby-hooks) and a shared primitives crate (gobby-core), updates gcode and gsqz behavior/docs accordingly, and adjusts CI/release automation (in addition to the Windows edition-2024 unsafe extern hotfix noted in the PR description).
Changes:
- Added
gobby-core(crates/gcore) for shared project discovery + bootstrap/daemon URL helpers, and migratedgcodecall sites to use it. - Added
ghook(crates/ghook, packagegobby-hooks) including spool-first transport, schemas, docs, and a release workflow; includes the Windowsunsafe extern "system"fix. - Updated
gsqzlow-savings behavior and reporting, hardenedgcodeLIKE escaping, and bumped crate versions + changelog/CI.
Reviewed changes
Copilot reviewed 41 out of 42 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/plans/sandbox-tolerant-hooks.md | Adds detailed multi-CLI sandbox-tolerant hooks plan/spec. |
| docs/plans/sandbox-tolerant-hooks-rust.md | Rust-side handoff plan describing gobby-core + ghook approach. |
| docs/guides/gsqz-user-guide.md | Documents /no-op and marker suppression behavior in gsqz. |
| docs/guides/ghook-user-guide.md | New user-facing guide for installing/wiring/diagnosing ghook. |
| docs/guides/ghook-development-guide.md | New developer guide describing ghook architecture, schema, and internals. |
| docs/guides/gcore-development-guide.md | New developer guide defining gobby-core API and design constraints. |
| docs/guides/gcode-development-guide.md | Updates gcode docs to reference gobby-core extraction. |
| crates/gsqz/src/primitives/group.rs | Refactors grouping sort to use sort_by_key(Reverse(..)). |
| crates/gsqz/src/main.rs | Uses CompressionResult::is_passthrough() to unify header/reporting rules. |
| crates/gsqz/src/compressor.rs | Implements low-savings marker suppression and /no-op strategy + tests. |
| crates/gsqz/Cargo.toml | Bumps gobby-squeeze version to 0.4.1. |
| crates/ghook/src/transport.rs | Implements spool-first enqueue + POST + quarantine + atomic write helper. |
| crates/ghook/src/terminal_context.rs | Captures/injects terminal context for selected hooks. |
| crates/ghook/src/main.rs | Adds ghook CLI modes (--gobby-owned/--diagnose/--version) and orchestration. |
| crates/ghook/src/envelope.rs | Defines v1 inbox envelope schema + schema-validation tests. |
| crates/ghook/src/diagnose.rs | Implements --diagnose output + schema-validation tests. |
| crates/ghook/src/detach.rs | Implements detach (setsid/FreeConsole); includes Windows 2024 unsafe extern fix. |
| crates/ghook/src/cli_config.rs | Defines per-CLI critical/terminal-context hook registries. |
| crates/ghook/schemas/inbox-envelope.v1.schema.json | Adds JSON Schema for inbox envelope v1. |
| crates/ghook/schemas/diagnose-output.v1.schema.json | Adds JSON Schema for diagnose output v1. |
| crates/ghook/README.md | Adds crate README describing CLI surface and schemas. |
| crates/ghook/Cargo.toml | Defines gobby-hooks crate metadata/deps; bumps to 0.1.1. |
| crates/gcore/src/project.rs | Adds shared project-root walk-up + project-id read helpers. |
| crates/gcore/src/lib.rs | Exposes bootstrap, daemon_url, and project modules. |
| crates/gcore/src/daemon_url.rs | Adds dial-url normalization for wildcard listen hosts + tests. |
| crates/gcore/src/bootstrap.rs | Adds bootstrap.yaml parsing with infallible defaults + tests. |
| crates/gcore/Cargo.toml | Defines gobby-core crate metadata/deps. |
| crates/gcode/src/search/fts.rs | Adds LIKE escaping helper and adjusts symbol resolution fallback behavior. |
| crates/gcode/src/project.rs | Removes duplicated project helpers now provided by gobby-core. |
| crates/gcode/src/config.rs | Migrates project root/id resolution to gobby-core::project. |
| crates/gcode/src/commands/init.rs | Simplifies warning emission match arm. |
| crates/gcode/src/commands/index.rs | Migrates project root/id resolution to gobby-core::project. |
| crates/gcode/src/commands/graph.rs | Deduplicates unresolved-symbol empty response path. |
| crates/gcode/Cargo.toml | Adds gobby-core dependency and bumps gobby-code version to 0.6.0. |
| README.md | Updates workspace overview and install instructions to include ghook/gobby-core. |
| Cargo.toml | Adds gcore/ghook to workspace members and release opt-level override. |
| Cargo.lock | Updates dependency graph for new crates/versions (but currently inconsistent for gobby-hooks). |
| CHANGELOG.md | Adds entries for gcode 0.6.0, gsqz 0.4.1, gobby-hooks 0.1.1/0.1.0, gobby-core 0.1.0. |
| .github/workflows/release-ghook.yml | Adds release workflow for gobby-hooks (ghook) binaries + crates.io publish. |
| .github/workflows/release-gcore.yml | Adds crates.io publish workflow for gobby-core. |
| .github/workflows/release-gcode.yml | Gates GitHub Release on crates.io publish (needs: [build, publish]). |
| .github/workflows/ci.yml | Adds dedicated clippy/test steps for gobby-core and gobby-hooks. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| .and_then(|v| v.as_str()) | ||
| .map(String::from) | ||
| .context("'id' field not found in .gobby/project.json") | ||
| } |
There was a problem hiding this comment.
gobby_core::project::{find_project_root, read_project_id} was moved here and the equivalent unit tests were removed from crates/gcode/src/project.rs. To avoid a coverage regression (and to keep this shared crate safe to evolve), please add unit tests in gcore covering: finding roots via project.json and gcode.json, returning None when absent, and read_project_id preferring id with fallback to project_id.
| } | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use super::{find_project_root, read_project_id}; | |
| use std::fs; | |
| use std::path::{Path, PathBuf}; | |
| use std::time::{SystemTime, UNIX_EPOCH}; | |
| struct TestDir { | |
| path: PathBuf, | |
| } | |
| impl TestDir { | |
| fn new() -> Self { | |
| let unique = format!( | |
| "gcore-project-tests-{}-{}", | |
| std::process::id(), | |
| SystemTime::now() | |
| .duration_since(UNIX_EPOCH) | |
| .expect("system time before unix epoch") | |
| .as_nanos() | |
| ); | |
| let path = std::env::temp_dir().join(unique); | |
| fs::create_dir_all(&path).expect("failed to create test directory"); | |
| Self { path } | |
| } | |
| fn path(&self) -> &Path { | |
| &self.path | |
| } | |
| } | |
| impl Drop for TestDir { | |
| fn drop(&mut self) { | |
| let _ = fs::remove_dir_all(&self.path); | |
| } | |
| } | |
| #[test] | |
| fn find_project_root_detects_project_json() { | |
| let temp = TestDir::new(); | |
| let root = temp.path().join("workspace"); | |
| let nested = root.join("a").join("b"); | |
| fs::create_dir_all(root.join(".gobby")).expect("failed to create .gobby"); | |
| fs::create_dir_all(&nested).expect("failed to create nested directories"); | |
| fs::write(root.join(".gobby").join("project.json"), r#"{"id":"proj-1"}"#) | |
| .expect("failed to write project.json"); | |
| let found = find_project_root(&nested); | |
| assert_eq!(found, Some(root)); | |
| } | |
| #[test] | |
| fn find_project_root_detects_gcode_json() { | |
| let temp = TestDir::new(); | |
| let root = temp.path().join("workspace"); | |
| let nested = root.join("src").join("deep"); | |
| fs::create_dir_all(root.join(".gobby")).expect("failed to create .gobby"); | |
| fs::create_dir_all(&nested).expect("failed to create nested directories"); | |
| fs::write(root.join(".gobby").join("gcode.json"), r#"{"name":"proj"}"#) | |
| .expect("failed to write gcode.json"); | |
| let found = find_project_root(&nested); | |
| assert_eq!(found, Some(root)); | |
| } | |
| #[test] | |
| fn find_project_root_returns_none_when_absent() { | |
| let temp = TestDir::new(); | |
| let start = temp.path().join("no").join("project").join("here"); | |
| fs::create_dir_all(&start).expect("failed to create start directory"); | |
| let found = find_project_root(&start); | |
| assert_eq!(found, None); | |
| } | |
| #[test] | |
| fn read_project_id_prefers_id_over_project_id() { | |
| let temp = TestDir::new(); | |
| let root = temp.path().join("workspace"); | |
| fs::create_dir_all(root.join(".gobby")).expect("failed to create .gobby"); | |
| fs::write( | |
| root.join(".gobby").join("project.json"), | |
| r#"{"id":"preferred-id","project_id":"legacy-id"}"#, | |
| ) | |
| .expect("failed to write project.json"); | |
| let id = read_project_id(&root).expect("failed to read project id"); | |
| assert_eq!(id, "preferred-id"); | |
| } | |
| #[test] | |
| fn read_project_id_falls_back_to_project_id() { | |
| let temp = TestDir::new(); | |
| let root = temp.path().join("workspace"); | |
| fs::create_dir_all(root.join(".gobby")).expect("failed to create .gobby"); | |
| fs::write( | |
| root.join(".gobby").join("project.json"), | |
| r#"{"project_id":"legacy-id"}"#, | |
| ) | |
| .expect("failed to write project.json"); | |
| let id = read_project_id(&root).expect("failed to read project id"); | |
| assert_eq!(id, "legacy-id"); | |
| } | |
| } |
| [workspace] | ||
| members = ["crates/gcode", "crates/gsqz", "crates/gloc"] | ||
| members = ["crates/gcode", "crates/gcore", "crates/ghook", "crates/gsqz", "crates/gloc"] | ||
| resolver = "3" |
There was a problem hiding this comment.
PR title/description frames this as a narrow Windows hotfix for gobby-hooks, but this PR also introduces gobby-core, adds the ghook crate/workflow/docs, bumps gcode to 0.6.0, and bumps gsqz to 0.4.1. Please either update the PR description/title to reflect the broader scope or split into smaller PRs so the hotfix can ship independently.
| let b64 = base64::engine::general_purpose::STANDARD.encode(stdin_bytes); | ||
| let body = serde_json::json!({ | ||
| "quarantined": true, | ||
| "stdin_bytes_b64": b64, | ||
| }); | ||
| atomic_write(&body_path, &serde_json::to_vec_pretty(&body)?)?; | ||
|
|
||
| let meta = serde_json::json!({ | ||
| "reason": "malformed_stdin", | ||
| "json_error": json_error, | ||
| "stdin_bytes_b64": b64, | ||
| }); | ||
| atomic_write(&meta_path, &serde_json::to_vec_pretty(&meta)?)?; |
There was a problem hiding this comment.
quarantine_malformed_at uses b64 in two serde_json::json! invocations, which will move the String the first time and fail to compile when reused for meta. Clone the value (or encode twice / store as &str) so both JSON objects can include the same base64 payload.
| pub fn atomic_write(final_path: &Path, bytes: &[u8]) -> Result<()> { | ||
| if let Some(parent) = final_path.parent() { | ||
| fs::create_dir_all(parent) | ||
| .with_context(|| format!("create_dir_all {}", parent.display()))?; | ||
| } | ||
| let mut tmp = final_path.to_path_buf(); | ||
| let mut name = tmp | ||
| .file_name() | ||
| .context("final path has no file name")? | ||
| .to_owned(); | ||
| name.push(".tmp"); | ||
| tmp.set_file_name(name); | ||
|
|
||
| { | ||
| let mut f = File::create(&tmp).with_context(|| format!("create tmp {}", tmp.display()))?; | ||
| f.write_all(bytes) | ||
| .with_context(|| format!("write tmp {}", tmp.display()))?; | ||
| f.sync_all() | ||
| .with_context(|| format!("fsync tmp {}", tmp.display()))?; | ||
| } | ||
| fs::rename(&tmp, final_path) | ||
| .with_context(|| format!("rename {} -> {}", tmp.display(), final_path.display()))?; | ||
| Ok(()) |
There was a problem hiding this comment.
atomic_write uses std::fs::rename(tmp, final_path) which will fail on Windows if final_path already exists (unlike POSIX rename-overwrite). This breaks rewriting stable paths like ~/.gobby/bin/.ghook-compatibility on subsequent runs, potentially leaving the daemon with stale compatibility info. Consider explicitly handling the destination-exists case (e.g., remove/replace semantics on Windows) for callers that overwrite a fixed filename.
| @@ -15,10 +28,7 @@ fn glob_to_like_prefix(pattern: &str) -> Option<String> { | |||
| if prefix.is_empty() { | |||
| None | |||
| } else { | |||
| Some(format!( | |||
| "{}%", | |||
| prefix.replace('%', r"\%").replace('_', r"\_") | |||
| )) | |||
| Some(format!("{}%", escape_like(&prefix))) | |||
| } | |||
| } | |||
There was a problem hiding this comment.
glob_to_like_prefix now escapes %, _, and \ with backslashes, but the SQL that consumes this value (e.g. cs.file_path LIKE ?) does not use an ESCAPE '\' clause. In SQLite, backslash is not an escape character unless ESCAPE is specified, so the escaping is currently ineffective and may produce incorrect prefilter behavior for paths containing these characters. Either add ESCAPE '\' wherever the prefix is used, or stop inserting backslashes here.
Summary
Hotfix for
gobby-hooksafter the 0.1.0 release. Bumps to 0.1.1.gobby-hooks 0.1.0published to crates.io fine, but thex86_64-pc-windows-msvcbuild target in the Release workflow failedbecause edition 2024 requires
extern "system" { ... }to beunsafe extern "system" { ... }. The#[cfg(windows)]path incrates/ghook/src/detach.rswas the only such block in theworkspace, and Mac/Linux dev never exercised it.
What's in the release
crates/ghook/src/detach.rs:36:unsafe extern "system" { fn FreeConsole() -> i32; }. One-line fix.crates/ghook/Cargo.tomlto 0.1.1.[0.1.1] — gobby-hookssection above the existing[0.1.0]entry.Test plan
cargo build -p gobby-hookscleancargo test -p gobby-hooks— 27 passingcargo clippy -p gobby-hooks --all-targets -- -D warningscleangobby-hooks-v0.1.1tag: Release ghook workflow succeeds across all 5 build targets and publishes a GitHub Release with binaries for all platforms🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
gobby-hooks, a new hook dispatcher for sandbox-tolerant AI CLI integration with enqueue-first delivery and automatic retry capability.gobby-coreshared library providing standardized project discovery and daemon configuration across all Gobby tools.Bug Fixes
Improvements